/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

	This file is part of eapmlib.

    Eapmlib is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or any 
	later version.

    Eapmlib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "PixelDiff.h"
#include "ImageData.h"

Evolutive::CPixelDiff::CPixelDiff(void) : CFeature()
{	
	//! Number of regions of the feature
	m_NumPoints=2;

#ifndef USE_OPENCV
	OPENCV_ERROR_CLASS("CPixelDiff");
#endif
}

Evolutive::CPixelDiff::~CPixelDiff(void)
{	
}

#ifdef USE_OPENCV
void Evolutive::CPixelDiff::SetParameters(int NumParameters, double *Parameters)
{
	int Polarity;
	register int i;
	unsigned int ParametersBuffer[9];

	// The parameters vector must have the following format:
	//
	//  |-------------------------------------------------------
	//  | Pixel1  | Pixel2 || Polarity || Weights || Channels  | 
	//  |-------------------------------------------------------
	//  |  0  1   | 2  3   ||    4     || 5    6  || 7    8    |
	//  |-------------------------------------------------------
	//  |  x  y   | x  y   ||    P     || W1  W2  || C1  C2    |
	//  |-------------------------------------------------------
	// Check the pointer
	if(!Parameters)
		throw CEvolutiveLibException("NULL pointer to the parameters",__FILE__,__LINE__,"SetParameters");

	// The dipoles can be defined with different number of parameters, using default values for not given values.
	if(NumParameters !=4 && NumParameters!=5 && NumParameters!=7 && NumParameters!=9)
		throw CEvolutiveLibException("Incorrect number of parameters",__FILE__,__LINE__,"SetParameters");

	// Store the double valued parameters to the unsigned int parameters array
	for(i=0;i<NumParameters;i++)
		ParametersBuffer[i]=static_cast<unsigned int>(Parameters[i]);

	// Obtain the information of the regions
	m_Pixels[0].Coord.x=ParametersBuffer[0];
	m_Pixels[0].Coord.y=ParametersBuffer[1];
	m_Pixels[1].Coord.x=ParametersBuffer[2];
	m_Pixels[1].Coord.y=ParametersBuffer[3];
	
	// Use the polarity information
	if(NumParameters>4)
	{
		Polarity=ParametersBuffer[4];
	}
	else
	{
		Polarity=0;
	}

	// Use the Weights information (this value came with a range [0,1], pass it to [1,2]
	if(NumParameters>5)
	{
		if(ParametersBuffer[5]>1 || ParametersBuffer[6]>1)
			throw CEvolutiveLibException("Feature Weight out of range",__FILE__,__LINE__,"SetParameters");
		m_Pixels[0].Weight=ParametersBuffer[5]+1;
		m_Pixels[1].Weight=ParametersBuffer[6]+1;
	}
	else
	{
		m_Pixels[0].Weight=1;
		m_Pixels[1].Weight=1;
	}

	// Use the channel information
	if(NumParameters>73)
	{
		m_Pixels[0].Channel=ParametersBuffer[7];
		m_Pixels[1].Channel=ParametersBuffer[8];
	}
	else
	{
		m_Pixels[0].Channel=0;
		m_Pixels[1].Channel=0;
	}

	// Apply the polarity value
	if(!Polarity)
	{		
		m_Pixels[1].Weight*=-1;
	}
	else
	{
		m_Pixels[0].Weight*=-1;
	}
}

bool Evolutive::CPixelDiff::IsValid(CvSize WinSize,bool UseColor)
{
	register int i;

	//! Verify all the regions
	for(i=0;i<m_NumPoints;i++)
	{
		//! Verify the coordinates
		if(m_Pixels[i].Coord.x<0 || m_Pixels[i].Coord.y<0 ||  		  
		   m_Pixels[i].Coord.x>=WinSize.width ||
		   m_Pixels[i].Coord.y>=WinSize.height)
		{
			return false;
		}

		//! Verify the channel
		if(m_Pixels[i].Channel>0 && !UseColor)
		{
			return false;
		}

		//! Verify the weights
		if(abs(m_Pixels[i].Weight)>2)
		{
			return false;
		}
	}

	return true;
}

void Evolutive::CPixelDiff::DrawFeature(IplImage *Image)
{
	bool UseColor;
	CvSize ImgSize;
	register int i;

	//! Obtain the input image size
	ImgSize=cvGetSize(Image);

	//! Obtain the color information
	if(Image->nChannels>1)
		UseColor=true;
	else
		UseColor=false;

	//! If the feature is a valid feature draw it
	if(IsValid(ImgSize,UseColor))
	{
		for(i=0;i<m_NumPoints;i++)
		{
			//! Draw the points
			if(UseColor)
			{
				if(m_Pixels[i].Weight>0)				
					cvCircle(Image,m_Pixels[i].Coord,3,CV_RGB(0,0,255),-1);
				else
					cvCircle(Image,m_Pixels[i].Coord,3,CV_RGB(255,0,0),-1);
			}
			else
			{
				if(m_Pixels[i].Weight>0)	
					cvCircle(Image,m_Pixels[i].Coord,3,CV_RGB(255,255,255),-1);
				else
					cvCircle(Image,m_Pixels[i].Coord,3,CV_RGB(0,0,0),-1);
			}
		}
	}
}

double Evolutive::CPixelDiff::Apply(CData *InputData)
{
	IplImage *pIntegral=NULL,*pRIntegral=NULL,*pSqIntegral=NULL;
	double ReturnVal;
	bool Valid=false;
	int Offset=0;

	if(InputData->GetDataName()==string("IMAGE") ||
		InputData->GetDataName()==string("INTEGRAL IMAGE"))
	{
		//! Point to the input data
		CImageData *pData=(CImageData*)InputData;

		//! Obtain a pointer to the image
		IplImage *pImage=pData->GetImage();

		// If a ROI is selected, calculate the offset in the integral images
		Offset=0;
		if(pData->GetOffset()>0)
		{
			Offset=((pData->GetROI()->y)*(pImage->widthStep))+pData->GetROI()->x;
		}

		//! Apply the method
		ReturnVal=Apply(pImage,Offset);
		
		//! Set the valid flag to true
		Valid=true;
	}

	//! Check if the data type is valid
	if(!Valid)
	{
		throw CEvolutiveLibException("Unsupported data type",__FILE__,__LINE__,"Apply");
	}

	return ReturnVal;
}

double Evolutive::CPixelDiff::Apply(IplImage *Image,int Offset,double Scale)
{
	register int i;
	unsigned int Disp;
	double Value,Aux;			
	unsigned char *pData=NULL;
	int WidthStep;
	int MaxDesp;

	// Verify the feature
	if(!IsValid(cvSize((Image->width-1)/Scale,(Image->height-1)/Scale),Image->nChannels>1))
		return 0.0;

	// Select the width step (in pixels, not in bytes)
	WidthStep=Image->widthStep;	
	MaxDesp=WidthStep*Image->height;

	// Point to the integral images
	pData=(unsigned char*)Image->imageData;
	
	// Get the feature value from the pixels intensity
	Value=0;
	for(i=0;i<m_NumPoints;i++)
	{
		// Calculate the displacements
		if(Scale!=1.0)
		{
			Disp=cvRound(Scale*(m_Pixels[i].Coord.x+m_Pixels[i].Coord.y*WidthStep))+Offset;
		}
		else
		{
			Disp=(m_Pixels[i].Coord.x+m_Pixels[i].Coord.y*WidthStep)+Offset;
		}

		// Check the displacements to avoid memory errors
		if(Disp>=MaxDesp)
			throw CEvolutiveLibException("Displacement out of range",__FILE__,__LINE__,"Apply");
#ifdef _TEST_COMP
cout << "    " << i << ".- [" << Disp << "] => ";
#endif
		// Apply the channel selection
		if(m_Pixels[i].Channel!=0)
		{
			throw CEvolutiveLibException("Color dipoles are not supported yet",__FILE__,__LINE__,"Apply");
		}
		
		// Evaluate the region
		Aux=pData[Disp];
		
#ifdef _TEST_COMP
cout << Aux;
#endif
		
#ifdef _TEST_COMP
cout << "(" << Aux << ")" << endl;
#endif

		// Apply the weight
		Aux*=m_Pixels[i].Weight;	

		// Add to the final value
		Value+=Aux;
	}
#ifdef _TEST_COMP
cout << "    Value = " << Value << endl;
#endif
	return Value;
}


Evolutive::CPixelDiffStruct* Evolutive::CPixelDiff::GetData(int &NumPixels)
{
	//! Set the number of regions
	NumPixels=m_NumPoints;

	//! Return a pointer to the regions
	return m_Pixels;
}

int Evolutive::CPixelDiff::GetNumParameters(void)
{
	return 9;
}

void Evolutive::CPixelDiff::GetParameters(double *Parameters)
{
	// The parameters vector have the following format:
	//
	//  |-------------------------------------------------------
	//  | Pixel1  | Pixel2 || Polarity || Weights || Channels  | 
	//  |-------------------------------------------------------
	//  |  0  1   | 2  3   ||    4     || 5    6  || 7    8    |
	//  |-------------------------------------------------------
	//  |  x  y   | x  y   ||    P     || W1  W2  || C1  C2    |
	//  |-------------------------------------------------------

	// Check the output pointer
	if(!Parameters)
		throw CEvolutiveLibException("NULL pointer to the parameters",__FILE__,__LINE__,"GetParameters");

	// Copy the parameters 
	Parameters[0]=static_cast<double>(m_Pixels[0].Coord.x);
	Parameters[1]=static_cast<double>(m_Pixels[0].Coord.y);	
	Parameters[2]=static_cast<double>(m_Pixels[1].Coord.x);
	Parameters[3]=static_cast<double>(m_Pixels[1].Coord.y);
	
	// Obtain the polarity value from the region weights
	if(m_Pixels[0].Weight>m_Pixels[1].Weight)
		Parameters[4]=0.0;
	else	
		Parameters[4]=1.0;

	// Take the absolute value of the weights (the polarity value contains the sign)
	Parameters[5]=static_cast<double>(abs(m_Pixels[0].Weight)-1);
	Parameters[6]=static_cast<double>(abs(m_Pixels[1].Weight)-1);
	
	Parameters[7]=static_cast<double>(m_Pixels[0].Channel);
	Parameters[8]=static_cast<double>(m_Pixels[1].Channel);
}

void Evolutive::CPixelDiff::Serialize(std::fstream &fs,bool Saving)
{
	// Call the base class method
	CFeature::Serialize(fs,Saving);

	// Implement the specialized code
	if(Saving)
	{
		// Save the data to a file
		// Save the class attributes
		fs.write((char*)&m_NumPoints,sizeof(int));	
		fs.write((char*)m_Pixels,m_NumPoints*sizeof(CPixelDiffStruct));
	}
	else
	{
		// Load the data from a file
		// Load the class attributes		
		fs.read((char*)&m_NumPoints,sizeof(int));	
		fs.read((char*)m_Pixels,m_NumPoints*sizeof(CPixelDiffStruct));
	}	
}
#endif //USE_OPENCV